package cool.webstudy.admin.service.impl.docker;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateNetworkResponse;
import com.github.dockerjava.api.exception.DockerException;
import com.github.dockerjava.api.model.Network;
import cool.webstudy.admin.constant.DockerConfigConstant;
import cool.webstudy.admin.constant.enums.ConfigTypeEnum;
import cool.webstudy.admin.constant.enums.rescode.ConfigResCodeEnum;
import cool.webstudy.admin.constant.enums.rescode.docker.NetworkResCodeEnum;
import cool.webstudy.admin.exception.BusinessException;
import cool.webstudy.admin.model.dto.config.CreateConfigDTO;
import cool.webstudy.admin.model.dto.config.UpdateConfigDTO;
import cool.webstudy.admin.model.po.config.docker.network.DockerNetworkConfigPO;
import cool.webstudy.admin.service.ConfigService;
import cool.webstudy.admin.service.docker.NetworkService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 莫振双
 * @date 2025/1/4 11:45
 * @description: 网络管理服务接口实现类
 */
@Service
public class NetworkServiceImpl implements NetworkService {
    @Autowired
    private DockerClient dockerClient;
    @Autowired
    private ConfigService configService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean isNetworkExist(String networkInfo) {
        if (StringUtils.isBlank(networkInfo)){
            throw new BusinessException(NetworkResCodeEnum.RC_NETWORK_INFO_MISSING);
        }
        //从数据库中查询docker网络配置
        String content = configService.queryConfig(ConfigTypeEnum.DOCKER_NETWORK);
        if (StringUtils.isBlank(content)){
            //如果在数据库中没有配置，则再查询docker中是否有配置
            try {
                List<Network> networkList = dockerClient.listNetworksCmd().exec();
                for (Network network : networkList) {
                    if (network.getName().equals(networkInfo) || network.getId().equals(networkInfo)) {
                        //如果有，则将数据同步到数据库中后再返回true
                        asyncNetworkToDatabase(network);
                        return true;
                    }
                }
            }catch (Exception e) {
                throw new BusinessException(NetworkResCodeEnum.RC_ERROR_QUERY,e.getMessage());
            }
            return false;
        }else{
            try {
                //反序列化配置信息，并将其应用至docker中
                ObjectMapper objectMapper = new ObjectMapper();
                DockerNetworkConfigPO dockerNetworkConfigPO = objectMapper.readValue(content, DockerNetworkConfigPO.class);
                String networkId = createNetworkToDocker(dockerNetworkConfigPO.getNetworkName(), dockerNetworkConfigPO.getSubnet(), dockerNetworkConfigPO.getGateway(), dockerNetworkConfigPO.getDriver(), dockerNetworkConfigPO.getParentDriver());
                //更新数据库中docker网络配置信息
                dockerNetworkConfigPO.setNetworkId(networkId);
                String newContent = objectMapper.writeValueAsString(dockerNetworkConfigPO);
                UpdateConfigDTO updateConfigDTO = new UpdateConfigDTO();
                updateConfigDTO.setType(ConfigTypeEnum.DOCKER_NETWORK);
                updateConfigDTO.setContent(newContent);
                configService.updateConfig(updateConfigDTO);
                return true;
            } catch (Exception e) {
                throw new BusinessException(ConfigResCodeEnum.RC_ERROR_ASYNC_CONFIG_TO_DB,e.getMessage());
            }
        }
    }

    @Override
    public Boolean createNetwork(String networkName, String subnet, String gateway, String driver, String parentDriver) {
        //判断网络配置是否存在
        if (isNetworkExist(networkName)){
            return true;
        }else{
            try {
                String networkId = createNetworkToDocker(networkName, subnet, gateway, driver, parentDriver);
                //将docker网络配置保存到数据库
                DockerNetworkConfigPO dockerNetworkConfigPO = new DockerNetworkConfigPO();
                dockerNetworkConfigPO.setNetworkName(networkName);
                dockerNetworkConfigPO.setSubnet(subnet);
                dockerNetworkConfigPO.setGateway(gateway);
                dockerNetworkConfigPO.setDriver(driver);
                dockerNetworkConfigPO.setParentDriver(parentDriver);
                dockerNetworkConfigPO.setNetworkId(networkId);
                //序列化
                ObjectMapper objectMapper = new ObjectMapper();
                CreateConfigDTO createConfigDTO = new CreateConfigDTO();
                createConfigDTO.setType(ConfigTypeEnum.DOCKER_NETWORK);
                createConfigDTO.setContent(objectMapper.writeValueAsString(dockerNetworkConfigPO));
                configService.createConfig(createConfigDTO);
                return true;
            } catch (DockerException e) {
                throw new BusinessException(NetworkResCodeEnum.RC_ERROR_CREATE,e.getMessage());
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }

        }

    }
    /**
     * @param networkName 网络名称
     * @param subnet 子网
     * @param gateway 网关
     * @param driver 驱动名称
     * @param parentDriver 父驱动
     * @return String 网络id
     * @description: 创建网络至docker
     */
    private String createNetworkToDocker(String networkName, String subnet, String gateway, String driver, String parentDriver) {
        //在docker中创建网络
        Network.Ipam.Config config = new Network.Ipam.Config();
        config.withGateway(gateway)
                .withSubnet(subnet);
        Network.Ipam ipam = new Network.Ipam();
        ipam.withConfig(config);
        Map<String, String> options = new HashMap<>();
        options.put(DockerConfigConstant.PARENT_DRIVER, parentDriver);
        CreateNetworkResponse createNetworkResponse = dockerClient.createNetworkCmd()
                .withName(networkName)
                .withDriver(driver)
                .withOptions(options)
                .withIpam(ipam)
                .exec();
        return createNetworkResponse.getId();
    }

    @Override
    public Boolean deleteNetwork(String networkInfo) {
        if (StringUtils.isBlank(networkInfo)){
            throw new BusinessException(NetworkResCodeEnum.RC_NETWORK_INFO_MISSING);
        }
        //TODO 从数据库中查找网络信息，获取网络id
        if (!isNetworkExist(networkInfo)){
            return false;
        }
        try {
            dockerClient.removeNetworkCmd(networkInfo).exec();
            return true;
        } catch (DockerException e) {
            throw new BusinessException(NetworkResCodeEnum.RC_ERROR_DELETE,e.getMessage());
        }
    }

    @Override
    public String queryNetworkName() {
        //查询是否存在网络配置信息
        try {
            String content = configService.queryConfig(ConfigTypeEnum.DOCKER_NETWORK);
            if (StringUtils.isNotBlank(content)){
                ObjectMapper objectMapper = new ObjectMapper();
                DockerNetworkConfigPO dockerNetworkConfigPO = objectMapper.readValue(content, DockerNetworkConfigPO.class);
                return dockerNetworkConfigPO.getNetworkName();
            }
            throw new BusinessException(NetworkResCodeEnum.RC_NETWORK_INFO_MISSING);
        } catch (JsonMappingException e) {
            throw new RuntimeException(e);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param network 获取到的容器网络配置
     * @return Boolean 是否同步成功
     * @description: 将网络信息同步到数据库中
     */
    private Boolean asyncNetworkToDatabase(Network network){
        DockerNetworkConfigPO dockerNetworkConfigPO = new DockerNetworkConfigPO();
        dockerNetworkConfigPO.setNetworkName(network.getName());
        dockerNetworkConfigPO.setSubnet(network.getIpam().getConfig().get(0).getSubnet());
        dockerNetworkConfigPO.setGateway(network.getIpam().getConfig().get(0).getGateway());
        dockerNetworkConfigPO.setDriver(network.getDriver());
        Map<String,String> options = network.getOptions();
        dockerNetworkConfigPO.setParentDriver(options.get(DockerConfigConstant.PARENT_DRIVER));
        dockerNetworkConfigPO.setNetworkId(network.getId());
        //序列化配置内容
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String content = objectMapper.writeValueAsString(dockerNetworkConfigPO);
            CreateConfigDTO dto = new CreateConfigDTO();
            dto.setType(ConfigTypeEnum.DOCKER_NETWORK);
            dto.setContent(content);
            configService.createConfig(dto);
            return true;
        } catch (Exception e) {
            throw new BusinessException(NetworkResCodeEnum.RC_ERROR_ASYNC_NETWORK_TO_DB);
        }
    }
}
